package NetWork;

import org.w3c.dom.ls.LSOutput;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

// UDP 版本的 回显客户端 (这里的API 还是和 服务端的是一样的)
public class UdpEchoClient {
    private DatagramSocket socket = null; // 有一个 socket 对象 来实现对网卡的操作, 因此来实现远程操控

    private String serverIp = null;
    private int serverPort = 0;
    // 一次通信, 需要有两个 ip 两个端口
    // 客户端的 ip 是 127.0.0.1 已知 (点分十进制), 而实际的ip是32位的整数
    // 客户端的 port 是系统自己分配的
    // 服务器的 ip 和 端口 也需要告诉客户段,才能顺利把这个消息发给服务器

    // 客户端的构造方法
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        socket = new DatagramSocket(); // 当前的socket对象, 是不需要显示绑定一个端口.(就是让操作系统自动分配一个端口)
        // 一次通信中, 涉及到的 ip 和 端口 有两组
        // 源 ip
        // 源 端口
        // 目的 ip
        // 目的端口
        // (端口号 用来标识 / 区分一个进程. 因此不允许一个端口同时被多个进程使用)
        // (通常情况下:socket 和端口号是一对一的, 进程和socket是多对多的)
        // 特殊情况下:(极端情况) : fork一个子进程 , 这是一个特殊的系统api,有一些特殊属性
        // 当然如果就想在客户端中手动指定端口是否可行呢? 不是完全不行,但是不推荐,有可能客户指定的这个端口号已经被用了
        // 这一冲突就可能导致程序无法正确通信了
        // 那么服务器这里的指定端口不怕重复呢?
        // 以为服务器是程序员自己手里的机器, 上面运行啥,都是程序员可控的. 程序员就可以安排哪个程序用哪个端口....这个是可控的
        // 目的是为了方便客户端找到服务器程序.
        // 客户端的机器是在用户手里的. 不同用户手里的机器,前期白怪, 上面运行着的程序各有不同,这种清情况是不可控的
        this.serverIp = serverIP;
        this.serverPort = serverPort;
    }
    // 启动客户端
    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        while(true){
            // 1. 从控制台读取要发送的数据
            System.out.println("> ");
            String request = scanner.next();
            if(request.equals("exit")){
                System.out.println("goodbye");
                break;
            }
            // 2.构造成UDP请求,并发送
            // 构造这个 packet 的时候 需要把 server 的ip 和 port 都传入进来, 但此处的 ip 地址 需要填写的是一个 32 位的 整数形式
            // 上面的ip 是字符串形式, 所以需要进行一个转换
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length
            , InetAddress.getByName(serverIp),serverPort);// 这里面还需要传入 Ip , Port 但是这里的IP 和 Port 是需要改动的 , 因为这里的是String 的
            // 此处是往 packet 中去构造的
            // 接下来进行发送
            socket.send(requestPacket);
            // 3. 读取服务器的UDP  响应 ,并解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            String response  = new String(responsePacket.getData(),0,responsePacket.getLength());
            // 4. 把解析好的结果显示出来
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);
        // 这里的端口要写服务器的端口一样
        // 然而 服务器是要服务多个客户端的
        // 而 idea 里面默认是只能启动一个客户端的
        // 如果要想启动多个客户端那就需要一个技巧
        // 点击运行idea右上角运行左边的选项 , 点击 Edit...
        // 然后在打开页面里面有一个
        // Modify options的蓝色选项, 然后 把 Allow multiple instances选中并应用就好了, 这就是可以多进程使用了
        // 当前的咱们的客户端和服务器程序都是在自己的主机上跑的,而网络存在的意义,就是跨主机通信
        // 咱们当前的程序,完全可以做到跨主机通信的!!
        // 但是还是不能把当前客户端的代码发给(比如老师),用"云服务器"这样的特殊电脑才可以.因为 这个 有外网ip,任何一个连上的网络设备都能访问
        // 而老师的ip是局域网,除非连同一个网线才可以访问
        client.start();
    }
}
